package com.ziytek.web.citizen.config.caffeine;

import com.github.benmanes.caffeine.cache.RemovalCause;
import com.google.gson.Gson;
import com.ziytek.web.citizen.caffeine.CachePutOrGet;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.constant.ServerConstant;
import com.ziytek.web.citizen.enums.StepTypeNoEnum;
import com.ziytek.web.citizen.job.SqliteSwitchJob;
import com.ziytek.web.citizen.mapper.OperationDetailMapper;
import com.ziytek.web.citizen.mapper.SettingDetailMapper;
import com.ziytek.web.citizen.model.bean.*;
import com.ziytek.web.citizen.pojo.basic.OperationDetail;
import com.ziytek.web.citizen.quartz.execute.StepIssuedEventListenerQuartzJob;
import com.ziytek.web.citizen.service.ChannelStatsService;
import com.ziytek.web.citizen.service.DeviceOfflineService;
import com.ziytek.web.citizen.service.JobTimesFinishService;
import com.ziytek.web.citizen.service.OperationVoCacheService;
import com.ziytek.web.citizen.service.itf.StepSettingsIssuedSingleService;
import com.ziytek.web.citizen.util.KeyUtil;
import com.ziytek.web.citizen.util.SpringBeanUtils;
import com.ziytek.web.citizen.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;

import java.util.Objects;

/**
 * @author fengh
 */
@Slf4j
public class CacheRemovalListener {

    private static Gson GSON = new Gson();
    private static OperationDetailMapper MAPPER;
    private static SqliteSwitchJob SQLITE_SWITCH;
    private static CachePutOrGet CACHE_PUT_OR_GET;
    private static SettingDetailMapper SET_MAPPER;
    private static ApplicationContext application;
    private static OperationDetailMapper OPERATION;
    private static DeviceOfflineService OFFLINE_SERVICE;
    private static ChannelStatsService CHANNEL_STATS_SERVICE;
    private static StepIssuedEventListenerQuartzJob QUARTZ_JOB;
    private static JobTimesFinishService JOB_TIMES_FINISH_SERVICE;
    private static OperationVoCacheService operationVoCacheService;
    private static StepSettingsIssuedSingleService stepSettingsIssuedSingleService;

    public static void cacheRemovalListenerInit() {
        ApplicationContext applicationContext = SpringBeanUtils.getApplicationContext();
        if (null == applicationContext) {
            return;
        }
        application = applicationContext;

        MAPPER = application.getBean(OperationDetailMapper.class);
        SQLITE_SWITCH = application.getBean(SqliteSwitchJob.class);
        SET_MAPPER = application.getBean(SettingDetailMapper.class);
        CACHE_PUT_OR_GET = application.getBean(CachePutOrGet.class);
        OPERATION = application.getBean(OperationDetailMapper.class);
        OFFLINE_SERVICE = application.getBean(DeviceOfflineService.class);
        CHANNEL_STATS_SERVICE = application.getBean(ChannelStatsService.class);
        QUARTZ_JOB = application.getBean(StepIssuedEventListenerQuartzJob.class);
        JOB_TIMES_FINISH_SERVICE = application.getBean(JobTimesFinishService.class);
        operationVoCacheService = application.getBean(OperationVoCacheService.class);
        stepSettingsIssuedSingleService = application.getBean(StepSettingsIssuedSingleService.class);
        log.info("CacheRemovalListener bean init success");
    }

    public static void onRemoval(Object key, Object value, RemovalCause cause) {
        if (null == MAPPER) {
            cacheRemovalListenerInit();
        }

        if (RemovalCause.EXPIRED == cause) {
            if (Objects.nonNull(key) && ((String) key).startsWith(CaffeineUtil.CACHE_KEY_COMMAND)) {
                log.info("工步指令缓存过期:{},value[{}]", key, GSON.toJson(value));
            }

            commandIssuedSuccessNext((String) key, value);
            sqliteSwitch(value);
            notNormalCache(value);

            if (Objects.nonNull(key) && ((String) key).startsWith(CaffeineUtil.OPERATION_DETAIL_DEL_KEY)) {
                if (!(value instanceof CaffeineUtil.CacheObject<?> cacheObject && cacheObject.data instanceof OperationDetail od)) {
                    return;
                }
                //log.info("删除工步详情:{}", GSON.toJson(od));
                OPERATION.deleteDetail(od);
            }

            // 电池设备离线消息延时处理
            if (Objects.nonNull(key) && ((String) key).startsWith(CaffeineUtil.CACHE_KEY_DEVICE_OFFLINE_YS)) {
                if (!(value instanceof CaffeineUtil.CacheObject<?> cacheObject && cacheObject.data instanceof String ip)) {
                    return;
                }

                // 查询是否在线
                Long time = CaffeineUtil.get(CaffeineUtil.CACHE_KEY_ONLINE + ip);
                boolean online = null != time && Math.abs(System.currentTimeMillis() - time) <= NumConstant.LONG_TEN * NumConstant.LONG_ONE_THOUSAND;
                if (online) {
                    log.info("设备离线在10秒内设备重新上线,不更新离线信息:{}-time:{}", ip, Math.abs(System.currentTimeMillis() - time) / 1000L);
                } else {
                    log.info("设备离线延时10秒后设备还未上线,更新离线信息:{}-time:{}", ip, null != time ? Math.abs(System.currentTimeMillis() - time) / 1000L : 0);
                    OFFLINE_SERVICE.deviceOffline(ip);
                }

            }
        }
    }

    private static void notNormalCache(Object value) {
        if (!(value instanceof CaffeineUtil.CacheObject<?> cacheObject && cacheObject.data instanceof FirstDataNotNormalCache c)) {
            return;
        }

        log.info("缓存KEY解析:{}", GSON.toJson(c));

        // 每个电池通道的工步操作缓存
        var opKey = KeyUtil.commandOperateKey(c.getCabinetNo(), c.getBoardNo(), c.getChannelNo());
        var vo = operationVoCacheService.getOperationVo(opKey);
        if (null == vo) {
            return;
        }

        var jobTimes = new JobTimes();
        jobTimes.setOrderNo(vo.getOrderNo());
        jobTimes.setBoardNo(vo.getBoardAddress());
        jobTimes.setChannelNo(vo.getCellNum());
        jobTimes.setCabinetNo(Integer.parseInt(vo.getCabinetNo()));
        jobTimes.setInterruptOrEndStepId(vo.getStepId());
        jobTimes.setSingeType(vo.getSingleType());
        jobTimes.setCommandType(vo.getSetType());

        var cacheKey = KeyUtil.jobTimesKey(jobTimes);

        // 完成，统计最后一笔数据
        log.info("统计最后一笔数据：opKey:{}-{}", opKey, GSON.toJson(jobTimes));
        //var statsCacheKey = c.getCabinetNo() + CaffeineUtil.CACHE_KEY_SYMBOL + c.getBoardNo() + CaffeineUtil.CACHE_KEY_SYMBOL+ c.getChannelNo();
        //var stats = CACHE_PUT_OR_GET.getStatsCache(statsCacheKey);

        QUARTZ_JOB.end(jobTimes, NumConstant.INT_TWO, NumConstant.INT_TWO);

        // 更新缓存
        vo.setStartFlag(ServerConstant.START_FLAG_FINISH);
        CaffeineUtil.set(opKey, vo, 24 * 60 * 60);
        CaffeineUtil.delete(cacheKey);
    }

    private static void sqliteSwitch(Object value) {
        if (!(value instanceof CaffeineUtil.CacheObject<?> cacheObject && cacheObject.data instanceof SqliteSwitchVo vo)) {
            return;
        }

        SQLITE_SWITCH.switched(vo.getTimes());
    }

    private static void commandIssuedSuccessNext(String key, Object value) {
        if (!(value instanceof CaffeineUtil.CacheObject<?> cacheObject && cacheObject.data instanceof CommandIssuedSuccess cs)) {
            return;
        }

        // KEY不是CaffeineUtil.CACHE_KEY_COMMAND开头的
        if (StringUtil.isEmpty(key) || !key.startsWith(CaffeineUtil.CACHE_KEY_COMMAND)) {
            log.info("缓存KEY不是CaffeineUtil.CACHE_KEY_COMMAND开头的不处理:{}", key);
            return;
        }

        // 执行终止指令，没有下一个工步方案
        if (cs.getStepId() != null && cs.getStepId() == NumConstant.LONG_LT_ZERO) {
            log.info("执行终止指令,没有下一个工步方案:{}", cs);
            return;
        }

        log.info("工步方案执行时间到key:{},下一个工步序号[{}],本次结束工艺:{}", key, cs.getOrder() + NumConstant.INT_ONE, GSON.toJson(cs));

        // 检查对应工步的电池数据最后一个时间的数据是否已经保存完成,未完成的延时执行,确保最后的数据统计
        Pair<Boolean, Integer> pair = JOB_TIMES_FINISH_SERVICE.jobTimesFinish(cs);
        if (!pair.getLeft() && pair.getRight() > NumConstant.INT_ZERO) {
            try {
                long sleepTime = pair.getRight() * 10 * 1000L + 800L;
                log.info("工步指令执行时间到但统计数据未保存完成,延时{}s后再次执行", sleepTime / 1000L);
                Thread.sleep(sleepTime);
            } catch (Exception e) {
                log.error("sleep error: ", e);
            }
        }

        // todo 放电工步指令, 保存放电容量数据
        saveDischargeStats(cs);

        // 不是最后一个工艺方案
        notLats(cs, cs.getStepId(), cs.getOrder());

        var commandIssued = new CommandIssuedSuccess();
        commandIssued.setInit(cs.getReqCabinetNo(), cs.getDoor(), cs.getStepId(), cs.getBoardNo(), cs.getChannelNo(), cs.getOrder());

        int order = cs.getOrder() + NumConstant.INT_ONE;

        boolean isAutomaticFinish = false;
        // 后台自己主动下发的结束指令,只下发指令，不做数据处理
        if (automaticFinish(cs.getStepId(), order)) {
            var only = new CommandIssuedSuccess();
            BeanUtils.copyProperties(commandIssued, only);
            only.setOrder(order);
            only.setStepId(NumConstant.LONG_LT_TEN);
            only.setInterruptOrEndStepId(cs.getStepId());
            stepSettingsIssuedSingleService.onlyIssue(only);
            isAutomaticFinish = true;
            // 延时2秒钟
            try {
                Thread.sleep(1000L);
            } catch (Exception e) {
                log.error("sleep error: ", e);
            }
        }

        if (isAutomaticFinish) {
            order = order + NumConstant.INT_ONE;
        }

        // 第二步、执行下一个工步方案 指令下发
        commandIssued.setOrder(order);
        commandIssued.setInterruptOrEndStepId(cs.getStepId());
        log.info("执行下一个工步方案:order[{}]->[{}],key={},执行工艺={}", order - NumConstant.INT_ONE, order, key, GSON.toJson(commandIssued));

        stepSettingsIssuedSingleService.nextIssued(commandIssued);
    }

    private static void saveDischargeStats(CommandIssuedSuccess cs) {
        var settings = GSON.fromJson(cs.getSettings(), AddSettings.class);
        if (settings == null) {
            return;
        }

        var isDischarge = (StepTypeNoEnum.STEP_TYPE_CCD_NUM.getCode().equalsIgnoreCase(settings.getSetType())
                || StepTypeNoEnum.STEP_TYPE_CPD_NUM.getCode().equalsIgnoreCase(settings.getSetType())
                || StepTypeNoEnum.STEP_TYPE_CRD_NUM.getCode().equalsIgnoreCase(settings.getSetType()));
        if (!isDischarge) {
            return;
        }

        //单个通道放电工艺
        if (cs.isSingleChannel()) {
            CHANNEL_STATS_SERVICE.singleStats(cs);
            return;
        }

        // 整体放电工艺
        CHANNEL_STATS_SERVICE.allChannelStats(cs);
    }

    private static void notLats(CommandIssuedSuccess cs, Long stepId, int order) {
        if (cs == null || cs.getDoor() == null) {
            return;
        }

        var sd = SET_MAPPER.queryListByStepIdDesc(stepId);
        if (sd == null || sd.getIsDelete() == null) {
            //log.info("commandIssuedEndEventListener,settingDetail is null,stepId:{}", stepId);
            return;
        }

        var lastOrder = sd.getSettingOrder();
        // 最后一个工步方案已经执行结束
        if (order == lastOrder - NumConstant.INT_ONE) {
            return;
        }

        // 单个通道工艺
        if (cs.isSingleChannel()) {
            var cacheDlKey = Integer.parseInt(cs.getReqCabinetNo()) + CaffeineUtil.CACHE_KEY_SYMBOL + cs.getBoardNo() + CaffeineUtil.CACHE_KEY_SYMBOL + cs.getChannelNo();
            //CACHE_PUT_OR_GET.deleteRealData(Integer.parseInt(cs.getReqCabinetNo()) + "");
            CACHE_PUT_OR_GET.delStatsCache(cacheDlKey);
            CACHE_PUT_OR_GET.deleteCurrent(cacheDlKey);
            CACHE_PUT_OR_GET.delInterruptStatsCache(cacheDlKey);
            CaffeineUtil.delete(CaffeineUtil.FIRST_DATA_LIST_KEY + cacheDlKey);
            return;
        }

        // 整体工步指令 第一步、删除实时数据缓存
        var operationDetailList = MAPPER.findDetailNotSingleType(String.valueOf(Integer.parseInt(String.valueOf(cs.getReqCabinetNo()))), ServerConstant.SINGLE_TYPE_ALL);
        if (CollectionUtils.isEmpty(operationDetailList)) {
            return;
        }

        for (var od : operationDetailList) {
            var cacheDlKey = Integer.parseInt(cs.getReqCabinetNo()) + CaffeineUtil.CACHE_KEY_SYMBOL + od.getBoardAddress() + CaffeineUtil.CACHE_KEY_SYMBOL + od.getCellNum();
            CACHE_PUT_OR_GET.delStatsCache(cacheDlKey);
            CACHE_PUT_OR_GET.deleteCurrent(cacheDlKey);
            CACHE_PUT_OR_GET.delInterruptStatsCache(cacheDlKey);
            CaffeineUtil.delete(CaffeineUtil.FIRST_DATA_LIST_KEY + cacheDlKey);
        }
    }

    private static boolean automaticFinish(Long stepId, Integer order) {
        var settingDetail = SET_MAPPER.queryListByStepIdAndSettingOrder(stepId, order);
        if (settingDetail == null) {
            return false;
        }

        return NumConstant.LONG_LT_TEN == settingDetail.getStepId();
    }
}
